﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using PinCity.Models.Repositories;

namespace PinCity.Models.Accounts
{
    /// <summary>
    /// A membership provider, based on UsersRepository.
    /// </summary>
    public class FluentMemberShipProvider : MembershipProvider
    {
        private readonly UsersRepository _repository = new UsersRepository();
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(typeof(FluentMemberShipProvider));

        public override string ApplicationName
        {
            get
            {
                return "PinCity";
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Change password of an existing user.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <returns>True if succsesfull.</returns>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            bool result = false;
            User tmp = _repository.GetUserByName(username);
            if (tmp != null)
            {
                if (tmp.Password.Equals(oldPassword))
                {
                    tmp.Password = newPassword;
                    _repository.SaveOrUpdate(tmp);
                    result = true;
                }
            }
            return result;
        }

        /// <summary>
        /// Not implemented.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="newPasswordQuestion"></param>
        /// <param name="newPasswordAnswer"></param>
        /// <returns></returns>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            return false;
        }

        /// <summary>
        /// Create a user and store it in the repository.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="email"></param>
        /// <param name="passwordQuestion"></param>
        /// <param name="passwordAnswer"></param>
        /// <param name="isApproved"></param>
        /// <param name="providerUserKey"></param>
        /// <param name="status"></param>
        /// <returns>Created MembershipUser or null, if not successfull</returns>
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            MembershipUser result = null;
            status = MembershipCreateStatus.ProviderError;
            logger.Debug("Trying to create user " + username + ".");
            if (!this.AbleToCreateUser(username, email))
            {
                logger.Debug("Unable to create user " + username + ".");
                status = MembershipCreateStatus.DuplicateUserName;
            }
            else
            {
                logger.Debug("Creating a user " + username + ".");
                User userToCreate = new User(
                    username,
                    password,
                    email,
                    DateTime.Now,
                    DateTime.MinValue, // Set last login date
                    DateTime.MinValue // Set last activity date
                    );
                logger.Debug("Storing new user to repository.");
                _repository.SaveOrUpdate(userToCreate);
                status = MembershipCreateStatus.Success;
                result = userToCreate; // implicit cast here
            }
            return result;
        }

        /// <summary>
        /// Checks, if it is possible to create a user with given
        /// username and email.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="email"></param>
        /// <returns>True, if possible.</returns>
        private bool AbleToCreateUser(string username, string email)
        {
            bool result = false;
            if (_repository.GetUserByName(username) == null)
            {
                result = true;
            }
            return result;
        }

        /// <summary>
        /// Deletes a user from repository.
        /// </summary>
        /// <param name="username">Username (login) of user to delete.</param>
        /// <param name="deleteAllRelatedData">Ignored for now.</param>
        /// <returns>True, in case of success</returns>
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            logger.Debug("Trying to delete user " + username + ".");
            bool result = false;
            User userToDelete = _repository.GetUserByName(username);
            if (userToDelete != null)
            {
                logger.Debug("Found user, trying to delete from repository.");
                _repository.Delete(userToDelete);
                result = true;
            }
            else 
            {
                logger.Debug("No user was found with username " + username + ".");
            }
            return result;
        }

        public override bool EnablePasswordReset
        {
            get { return false; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return false; }
        }

        /// <summary>
        /// Returns set of user with email, that matches the given one.
        /// </summary>
        /// <param name="emailToMatch">Email of desired users.</param>
        /// <param name="pageIndex">IGNORED</param>
        /// <param name="pageSize">IGNORED</param>
        /// <param name="totalRecords">Total found users.</param>
        /// <returns></returns>
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection result = new MembershipUserCollection();
            IList<User> users = _repository.GetUsersByEmail(emailToMatch);
            foreach (var user in users)
            {
                result.Add(user); // implicit cast here
            }
            totalRecords = result.Count;
            return result;
        }

        /// <summary>
        /// Returns set of user with login, that matches the given one.
        /// </summary>
        /// <param name="usernameToMatch">Login of desired users.</param>
        /// <param name="pageIndex">IGNORED</param>
        /// <param name="pageSize">IGNORED</param>
        /// <param name="totalRecords">Total found users.</param>
        /// <returns></returns>
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection result = new MembershipUserCollection();
            IList<User> users = _repository.GetUsersByName(usernameToMatch);
            foreach (var user in users)
            {
                result.Add(user); // implicit cast here
            }
            totalRecords = result.Count;
            return result;
        }

        /// <summary>
        /// Returns all registered users from repository.
        /// </summary>
        /// <param name="pageIndex">IGNORED</param>
        /// <param name="pageSize">IGNORED</param>
        /// <param name="totalRecords">Count of all users.</param>
        /// <returns></returns>
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection result = new MembershipUserCollection();
            IList<User> users = _repository.GetAll();
            foreach (var user in users)
            {
                result.Add(user); // implicit cast here
            }
            totalRecords = result.Count;
            return result;
        }

        public override int GetNumberOfUsersOnline()
        {
            return 0;
        }

        public override string GetPassword(string username, string answer)
        {
            string result = "";
            User user = _repository.GetUserByName(username);
            if (user != null)
            {
                result = user.Password;
            }
            return result;
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            return _repository.GetUserByName(username) ?? new User(); // implicit cast here
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            return _repository.Get((uint)providerUserKey) ?? new User(); // implicit cast here
        }

        public override string GetUserNameByEmail(string email)
        {
            User tmp = _repository.GetUserByEmail(email);
            return tmp == null ? "" : tmp.Login;
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return 1; }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return 0; }
        }

        public override int MinRequiredPasswordLength
        {
            get { return 0; }
        }

        public override int PasswordAttemptWindow
        {
            get { return 10; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return MembershipPasswordFormat.Hashed; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get { return ""; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return false; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return false; }
        }

        /// <summary>
        /// Not implemented.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="answer"></param>
        /// <returns></returns>
        public override string ResetPassword(string username, string answer)
        {
            return "";
        }

        /// <summary>
        /// Not implemented.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public override bool UnlockUser(string userName)
        {
            return true;
        }

        /// <summary>
        /// Not implemented.
        /// </summary>
        /// <param name="user"></param>
        public override void UpdateUser(MembershipUser user)
        {
                        
        }

        /// <summary>
        /// Checks, if username and password are correct.
        /// </summary>
        /// <param name="username">Username to check.</param>
        /// <param name="password">Password to check.</param>
        /// <returns>True, if there is such user with such password.</returns>
        public override bool ValidateUser(string username, string password)
        {
            bool result = false;
            User tmp = _repository.GetUserByName(username);
            if (tmp != null)
            {
                result = tmp.Password.Equals(password);
            }
            return result;
        }
    }
}